package com.xzzz.irda.tracker.server;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.xzzz.common.base.exception.XzException404;
import com.xzzz.common.base.pojo.PageRes;
import com.xzzz.common.base.util.BeanUtil;
import com.xzzz.common.base.util.DateUtils;
import com.xzzz.common.base.util.json.JsonUtil;
import com.xzzz.irda.tracker.core.SpanNode;
import com.xzzz.irda.tracker.core.adapter.aspect.TrackerStart;
import com.xzzz.irda.tracker.core.common.TrackerConstants;
import com.xzzz.irda.tracker.core.repository.TrackerRepository;
import com.xzzz.irda.tracker.server.mappers.TrackerMetricMySqlMapper;
import com.xzzz.irda.tracker.server.pojo.SpanNodeEntity;
import com.xzzz.irda.tracker.server.pojo.SpanNodeTermsRes;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.util.*;
import java.util.stream.Collectors;

/**
 * tracker 的各项信息由 mysql 保存.
 * <p
 */
@Slf4j
@Service
public class TrackerMetricServiceByMySql implements TrackerRepository, TrackerMetricService {

    public TrackerMetricServiceByMySql() {
        log.info("[TRACKERS] 开启追踪信息通过配置MySQL持久化");
    }

    @Autowired(required = false)
    private TrackerMetricMySqlMapper mapper;

    @PostConstruct
    public void init() {
        if (mapper == null) {
            log.error("未扫描到 [TrackerMetricMySqlMapper], tracker 信息无法持久化");
        }
    }

    @Override
    public boolean save(List<SpanNode> spanNodes) {
        List<SpanNodeEntity> entities = new ArrayList<>();
        for (SpanNode spanNode : spanNodes) {
            SpanNodeEntity entity = new SpanNodeEntity();
            BeanUtil.toObj(spanNode, entity);
            entity.setSpanStart(DateUtils.date(spanNode.getSpanStart()));
            entity.setSpanEnd(DateUtils.date(spanNode.getSpanEnd()));

            // records 特殊处理
            if (spanNode.getRecords() == null) {
                entity.setRecords("");
            } else {
                entity.setRecords(JsonUtil.toJson(spanNode.getRecords()));
                if (entity.getRecords().length() > 5000) {
                    entity.setRecords(entity.getRecords().substring(0, 4999));
                }
            }

            entities.add(entity);
        }
        mapper.save(entities);
        return true;
    }

    @Override
    @TrackerStart
    @Scheduled(cron = "0 0/1 * * * ?")
    public long expire() {
        long start = System.currentTimeMillis();
        Date today = DateUtil.date();
        // 删除2天前的
        Date expireDate = DateUtil.offsetHour(today, -12);
        String expireDateStr = DateUtil.format(expireDate, "yyyy-MM-dd HH:mm:ss");

        QueryWrapper<SpanNodeEntity> query = new QueryWrapper<>();
        query.le("span_start", expireDate);
        int num = mapper.delete(query);
        log.info("[TRACKERS] 删除小于 [{}] SPAN 信息, 共:[{}], 用时: {}", expireDateStr, num, System.currentTimeMillis() - start);
        return num;
    }

    @Override
    public void saveBatch(List<SpanNode> spanNodes) {
        this.save(spanNodes);
    }

    /**
     * 查询一次追踪下的全部信息
     *
     * @param traceId traceId
     * @return span集合
     */
    @Override
    public List<SpanNode> list(String traceId) {
        QueryWrapper<SpanNodeEntity> query = new QueryWrapper<>();
        query.eq("trace_id", traceId).orderByDesc("span_start").orderByDesc("span_start_equal_id");
        List<SpanNodeEntity> list = mapper.selectList(query);

        List<SpanNode> result = new LinkedList<>();
//        List<SpanNode> sortSpans = new ArrayList<>();

        for (SpanNodeEntity entity : list) {
            SpanNode span = new SpanNode();
            span.setSpanStart(entity.getSpanStart().getTime());
            span.setSpanEnd(entity.getSpanEnd().getTime());
            BeanUtil.toObj(entity, span);
            try {
                span.setRecords(JsonUtil.toObj(entity.getRecords(), TreeMap.class));
            } catch (Exception e) {
                log.warn("[TRACKERS] trace records 信息转换失败:" + e.getMessage());
            }
            result.add(span);
//            if (span.getSpanParentId().equals(TrackerConstants.ROOT_SPAN)) {
//            } else if (CollUtil.isNotEmpty(sortSpans) &&
//                    span.getSpanParentId().equals(sortSpans.get(sortSpans.size() - 1).getSpanId())) {
//                sortSpans.add(span);
//            } else {
//                spans.add(span);
//            }
        }


//        for (int i = 0; i < list.size() || CollUtil.isEmpty(spans); i++) {
//            Iterator<SpanNode> iterator = spans.iterator();
//            while (iterator.hasNext()) {
//                SpanNode span = iterator.next();
//                if (span.getSpanParentId().equals(sortSpans.get(sortSpans.size() - 1).getSpanId())) {
//                    sortSpans.add(span);
//                    iterator.remove();
//                }
//            }
//        }

        return result;
    }

    @Override
    public PageRes<SpanNode> pages(Integer pageNum, Integer pageSize, String appName, String traceId) {
        return null;
    }

    @Override
    public List<SpanNodeTermsRes> listGroupByTraceId(Integer pageSize, String appName, String traceId) {
        LambdaQueryWrapper<SpanNodeEntity> where = new LambdaQueryWrapper<SpanNodeEntity>()
                .eq(SpanNodeEntity::getSpanParentId, TrackerConstants.ROOT_SPAN)
                .orderByDesc(SpanNodeEntity::getSpanStart)
                .last("limit " + pageSize);

        if (StrUtil.isNotBlank(appName)) {
            where.eq(SpanNodeEntity::getAppName, appName);
        }
        if (StrUtil.isNotBlank(traceId)) {
            where.eq(SpanNodeEntity::getTraceId, traceId);
        }

        List<SpanNodeEntity> traces = mapper.selectList(where);
        XzException404.throwBy(CollUtil.isEmpty(traces), "数据不存在或尚未持久化, 请稍后再试.");
        List<String> traceIds = traces.stream().map(SpanNodeEntity::getTraceId).collect(Collectors.toList());

        where = new LambdaQueryWrapper<SpanNodeEntity>().in(SpanNodeEntity::getTraceId, traceIds);
        List<SpanNodeEntity> spans = mapper.selectList(where);

        Map<String, List<SpanNodeEntity>> spanMaps = spans.stream().collect(Collectors.groupingBy(SpanNodeEntity::getTraceId));

        List<SpanNodeTermsRes> result = new ArrayList<>();
        for (SpanNodeEntity trace : traces) {
            List<SpanNodeEntity> curSpans = spanMaps.get(trace.getTraceId());
            if (CollUtil.isEmpty(curSpans)) {
                curSpans = new ArrayList<>();
            }

            SpanNodeTermsRes res = new SpanNodeTermsRes();
            res.setTraceId(trace.getTraceId());
            res.setSpanStart(DateUtils.toYMDHMS(trace.getSpanStart()));
            res.setAppName(trace.getAppName());
            res.setSpanName(trace.getSpanName());
            res.setSpanType(trace.getSpanType());

            res.setNodeCount((long) curSpans.size());
            SpanNodeEntity minDate = curSpans.stream().min(Comparator.comparing(SpanNodeEntity::getSpanStart)).get();
            SpanNodeEntity maxDate = curSpans.stream().max(Comparator.comparing(SpanNodeEntity::getSpanEnd)).get();
            res.setSpanInterval(DateUtils.between(minDate.getSpanStart(), maxDate.getSpanEnd(), DateUnit.MS));
            result.add(res);
        }
        return result;
        //
//        return mapper.listGroupByTraceId(appName, traceId);


    }
}
